package com.lkd.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.collect.Lists;
import com.lkd.common.VMSystem;
import com.lkd.config.TopicConfig;
import com.lkd.contract.VendoutContract;
import com.lkd.contract.VendoutData;
import com.lkd.dao.OrderDao;
import com.lkd.emq.MqttConfig;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.OrderEntity;
import com.lkd.exception.LogicException;
import com.lkd.feign.UserService;
import com.lkd.feign.VMService;
import com.lkd.service.OrderService;
import com.lkd.vo.*;
import com.lkd.wxpay.WxPayDTO;
import com.lkd.wxpay.WxPaySDKUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Autowired
    private RestHighLevelClient esClient;
    @Autowired
    private VMService vmService;
    @Autowired
    private UserService userService;
    @Autowired
    private WxPaySDKUtil wxPaySDKUtil;
    @Resource
    private MqttProducer mqttProducer;
    @Autowired
    private RedissonClient redissonClient;


    @Override
    public OrderEntity getByOrderNo(String orderNo) {
        QueryWrapper<OrderEntity> qw = new QueryWrapper<>();
        qw.lambda()
                .eq(OrderEntity::getOrderNo, orderNo);
        return this.getOne(qw);
    }


    @Override
    public Pager<OrderVO> search(Integer pageIndex, Integer pageSize, String orderNo, String openId, String startDate, String endDate) {
        return Pager.buildEmpty();
    }


    @Override
    public List<Long> getTop10Sku(Integer businessId) {

        SearchRequest searchRequest = new SearchRequest("order");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //查询条件：最近三个月

        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("update_time");
        rangeQueryBuilder.gte(LocalDateTime.now().plusMonths(-3).format(DateTimeFormatter.ISO_DATE_TIME));
        rangeQueryBuilder.lte(LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(rangeQueryBuilder);

        boolQueryBuilder.must(QueryBuilders.termQuery("business_id", businessId));
        sourceBuilder.query(boolQueryBuilder);

        AggregationBuilder orderAgg = AggregationBuilders.terms("sku").field("sku_id")
                .subAggregation(AggregationBuilders.count("count").field("sku_id"))
                .order(BucketOrder.aggregation("count", false))
                .size(10);

        sourceBuilder.aggregation(orderAgg);
        searchRequest.source(sourceBuilder);

        try {
            SearchResponse searchResponse = esClient.search(searchRequest, RequestOptions.DEFAULT);
            Aggregations aggregations = searchResponse.getAggregations();
            if (aggregations == null) {
                return Lists.newArrayList();
            }

            var term = (Terms) aggregations.get("sku");
            var buckets = term.getBuckets();

            return buckets.stream().map(b -> Long.valueOf(b.getKey().toString())).collect(Collectors.toList());

        } catch (IOException e) {
            e.printStackTrace();
            return Lists.newArrayList();
        }

    }

    /**
     * 下单请求(创建订单)
     *
     * @param payVO
     * @return
     */
    @Override
    public String requestPay(PayVO payVO) throws JsonProcessingException, InterruptedException {
        //在一台售货机上，不能两人同时购买，必须在第一个购物流程结束后，才能开始第2个购物流程
        //使用redission加锁
        RLock lock = redissonClient.getLock(String.format(VMSystem.VM_LOCK_KEY_PREF, payVO.getInnerCode()));
        if (!lock.tryLock(0L,2L, TimeUnit.MINUTES)) {
            throw new LogicException("设备出货中，请稍后");
        }
        OrderEntity orderEntity = new OrderEntity();
        if (!StringUtils.isNotEmpty(payVO.getInnerCode())) {
            throw new LogicException("参数错误");
        }
        //判断openid
        if (payVO.getOpenId() == null) {
            throw new LogicException("OpenID为空");
        }
        //判断是否有库存
        Boolean capacity = vmService.hasCapacity(payVO.getInnerCode(), Long.parseLong(payVO.getSkuId()));
        if (!capacity) {
            throw new LogicException("当前商品库存不足");
        }
        //订单实体构建
        OrderEntity order = order(payVO);
        WxPayDTO wxPayDTO = new WxPayDTO();
        wxPayDTO.setBody(order.getSkuName());
        wxPayDTO.setOpenid(order.getOpenId());
        wxPayDTO.setExpireSecond(600);
        wxPayDTO.setOutTradeNo(order.getOrderNo());
        wxPayDTO.setTotalFee(order.getAmount());
        //保存订单
        save(order);
        //如果用户下单后长时间未支付,则需要将订单置为无效状态.
        //延迟消息方式
        mqttProducer.send("$delayed/600"+ TopicConfig.ORDER_CHECK_TOPIC,2,order);
        return wxPaySDKUtil.requestPay(wxPayDTO);

    }

    /**
     * 出货请求
     *
     * @param validPay
     */
    @Override
    public void vendOut(String validPay) throws JsonProcessingException {
        //1.根据订单编号查找对应订单详情
        OrderEntity order = getByOrderNo(validPay);
        //1.2更改订单状态/支付状态为已支付
        order.setStatus(VMSystem.ORDER_STATUS_PAYED);
        order.setPayStatus(VMSystem.PAY_STATUS_PAYED);
        this.updateById(order);
        //1.3发送消息到EMQ
        VendoutContract contract =new VendoutContract();
        VendoutData vendoutData =new VendoutData();
        contract.setVendoutData(vendoutData);
        contract.setInnerCode(order.getInnerCode());
        //向售货机微服务 发送出出货请求
        mqttProducer.send(TopicConfig.VMS_VENDOUT_TOPIC,2,contract);
    }

    /**
     * 构建订单实体
     *
     * @param payVO
     */
    private OrderEntity order(PayVO payVO) {
        OrderEntity orderEntity = new OrderEntity();
        SkuVO skuVO = vmService.getSku(payVO.getSkuId());
        VmVO vmVO = vmService.getVMInfo(payVO.getInnerCode());
        //订单编号  机器编号 加上当前时间纳秒
        //String str = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
        orderEntity.setOrderNo(vmVO.getInnerCode() + System.nanoTime());
        //机器编号
        orderEntity.setInnerCode(payVO.getInnerCode());
        //商品id
        orderEntity.setSkuId((Long.parseLong(payVO.getSkuId())));
        //商品名称
        orderEntity.setSkuName(vmService.getSku(payVO.getSkuId()).getSkuName());
        //商品价格
        orderEntity.setPrice(skuVO.getPrice());
        //分类id
        orderEntity.setClassId(skuVO.getClassId());
        //订单状态
        orderEntity.setStatus(VMSystem.ORDER_STATUS_CREATE);
        //支付状态
        orderEntity.setPayStatus(VMSystem.PAY_STATUS_NOPAY);
        //支付类型
        orderEntity.setPayType(Integer.toString(2));

        //订单金额
        //如果有折扣
        if (skuVO.isDiscount()) {
            orderEntity.setAmount(vmService.discount(skuVO.getSkuId()));
        }
        orderEntity.setAmount(vmService.getSku(payVO.getSkuId()).getPrice());
        //点位地址
        orderEntity.setAddr(Long.toString(vmVO.getNodeId()));
        //区域id
        orderEntity.setRegionId(vmVO.getRegionId());
        //区域name
        orderEntity.setRegionName(vmVO.getRegionName());
        //商圈id
        orderEntity.setBusinessId(vmVO.getBusinessId());
        //商圈名称
        orderEntity.setBusinessName(vmVO.getRegionName());
        //合作商id
        orderEntity.setOwnerId(vmVO.getOwnerId());
        orderEntity.setOpenId(payVO.getOpenId());
        orderEntity.setNodeId(vmVO.getNodeId());
        orderEntity.setNodeName(vmVO.getNodeName());
        orderEntity.setCreateTime(LocalDateTime.now());
        orderEntity.setUpdateTime(LocalDateTime.now());

        //设置合作商账单分账金额
        PartnerVO partner = userService.getPartner(vmVO.getOwnerId());
        BigDecimal bg = new BigDecimal(skuVO.getRealPrice());
        int bill = bg.multiply(new BigDecimal(partner.getRatio())).divide(new BigDecimal(100), 0, RoundingMode.HALF_UP).intValue();
        orderEntity.setBill(bill);
        this.save(orderEntity);
        return orderEntity;
    }
}
